<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
<head>
  <link href="//gmpg.org/xfn/11" rel="profile">
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="generator" content="Hugo 0.68.3" />

  
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>redis分布式锁 &middot; 我的博客</title>

  
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/print.css" media="print">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/poole.css">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/syntax.css">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/hyde.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Abril+Fatface|PT+Sans:400,400i,700">


  
  <link rel="apple-touch-icon-precomposed" sizes="144x144" href="/apple-touch-icon-144-precomposed.png">
  <link rel="shortcut icon" href="/favicon.png">

  
  
</head>

  <body class="theme-base-0b ">
  <aside class="sidebar">
  <div class="container sidebar-sticky">
    <div class="sidebar-about">
      <a href="/my_technology_blog/"><h1>我的博客</h1></a>
      <p class="lead">
       杨博的博客 
      </p>
    </div>

    <nav>
      <ul class="sidebar-nav">
        <li><a href="/my_technology_blog/">Home</a> </li>
        
      </ul>
    </nav>

    <p>&copy; 2021. All rights reserved. </p>
  </div>
</aside>

    <main class="content container">
    <div class="post">
  <h1>redis分布式锁</h1>
  <time datetime=2020-06-17T16:23:42&#43;0800 class="post-date">Wed, Jun 17, 2020</time>
  <p>1、选用Redis实现分布式锁原因：</p>
<p>（1）Redis有很高的性能；
（2）Redis命令对此支持较好，实现起来比较方便</p>
<p>2、使用命令介绍：</p>
<p>（1）SETNX</p>
<p>SETNX key val：当且仅当key不存在时，set一个key为val的字符串，返回1；若key存在，则什么都不做，返回0。</p>
<p>（2）expire</p>
<p>expire key timeout：为key设置一个超时时间，单位为second，超过这个时间锁会自动释放，避免死锁。</p>
<p>（3）delete</p>
<p>delete key：删除key</p>
<p>在使用Redis实现分布式锁的时候，主要就会使用到这三个命令。</p>
<p>3、实现思想：</p>
<p>（1）获取锁的时候，使用setnx加锁，并使用expire命令为锁添加一个超时时间，超过该时间则自动释放锁，锁的value值为一个随机生成的UUID，通过此在释放锁的时候进行判断。</p>
<p>（2）获取锁的时候还设置一个获取的超时时间，若超过这个时间则放弃获取锁。</p>
<p>（3）释放锁的时候，通过UUID判断是不是该锁，若是该锁，则执行delete进行锁释放。</p>
<p>4、 分布式锁的简单实现代码：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#75715e">#连接redis</span>
<span style="color:#f92672">import</span> time
<span style="color:#f92672">import</span> uuid
<span style="color:#f92672">from</span> threading <span style="color:#f92672">import</span> Thread

<span style="color:#f92672">import</span> redis

redis_client <span style="color:#f92672">=</span> redis<span style="color:#f92672">.</span>Redis(host<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;localhost&#34;</span>,
                           port<span style="color:#f92672">=</span><span style="color:#ae81ff">6379</span>,
                           <span style="color:#75715e"># password=123,</span>
                           db<span style="color:#f92672">=</span><span style="color:#ae81ff">10</span>)

<span style="color:#75715e">#获取一个锁</span>
<span style="color:#75715e"># lock_name：锁定名称</span>
<span style="color:#75715e"># acquire_time: 客户端等待获取锁的时间</span>
<span style="color:#75715e"># time_out: 锁的超时时间</span>
<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">acquire_lock</span>(lock_name, acquire_time<span style="color:#f92672">=</span><span style="color:#ae81ff">10</span>, time_out<span style="color:#f92672">=</span><span style="color:#ae81ff">10</span>):
    <span style="color:#e6db74">&#34;&#34;&#34;获取一个分布式锁&#34;&#34;&#34;</span>
    identifier <span style="color:#f92672">=</span> str(uuid<span style="color:#f92672">.</span>uuid4())
    end <span style="color:#f92672">=</span> time<span style="color:#f92672">.</span>time() <span style="color:#f92672">+</span> acquire_time
    lock <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;string:lock:&#34;</span> <span style="color:#f92672">+</span> lock_name
    <span style="color:#66d9ef">while</span> time<span style="color:#f92672">.</span>time() <span style="color:#f92672">&lt;</span> end:
        <span style="color:#66d9ef">if</span> redis_client<span style="color:#f92672">.</span>setnx(lock, identifier):
            <span style="color:#75715e"># 给锁设置超时时间, 防止进程崩溃导致其他进程无法获取锁</span>
            redis_client<span style="color:#f92672">.</span>expire(lock, time_out)
            <span style="color:#66d9ef">return</span> identifier
        <span style="color:#66d9ef">elif</span> <span style="color:#f92672">not</span> redis_client<span style="color:#f92672">.</span>ttl(lock):
            redis_client<span style="color:#f92672">.</span>expire(lock, time_out)
        time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">0.001</span>)
    <span style="color:#66d9ef">return</span> False

<span style="color:#75715e">#释放一个锁</span>
<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">release_lock</span>(lock_name, identifier):
    <span style="color:#e6db74">&#34;&#34;&#34;通用的锁释放函数&#34;&#34;&#34;</span>
    lock <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;string:lock:&#34;</span> <span style="color:#f92672">+</span> lock_name
    pip <span style="color:#f92672">=</span> redis_client<span style="color:#f92672">.</span>pipeline(True)
    <span style="color:#66d9ef">while</span> True:
        <span style="color:#66d9ef">try</span>:
            pip<span style="color:#f92672">.</span>watch(lock)
            lock_value <span style="color:#f92672">=</span> redis_client<span style="color:#f92672">.</span>get(lock)
            <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> lock_value:
                <span style="color:#66d9ef">return</span> True

            <span style="color:#66d9ef">if</span> lock_value<span style="color:#f92672">.</span>decode() <span style="color:#f92672">==</span> identifier:
                pip<span style="color:#f92672">.</span>multi()
                pip<span style="color:#f92672">.</span>delete(lock)
                pip<span style="color:#f92672">.</span>execute()
                <span style="color:#66d9ef">return</span> True
            pip<span style="color:#f92672">.</span>unwatch()
            <span style="color:#66d9ef">break</span>
        <span style="color:#66d9ef">except</span> redis<span style="color:#f92672">.</span>excetions<span style="color:#f92672">.</span>WacthcError:
            <span style="color:#66d9ef">pass</span>
    <span style="color:#66d9ef">return</span> False
</code></pre></div><p><strong>5、测试刚才实现的分布式锁</strong></p>
<p>例子中使用50个线程模拟秒杀10张票，使用–运算符来实现商品减少，从结果有序性就可以看出是否为加锁状态。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python">count<span style="color:#f92672">=</span><span style="color:#ae81ff">10</span>

<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">seckill</span>(i):
    identifier<span style="color:#f92672">=</span>acquire_lock(<span style="color:#e6db74">&#39;resource&#39;</span>)
    <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#34;线程:{}--获得了锁&#34;</span><span style="color:#f92672">.</span>format(i))
    time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">1</span>)
    <span style="color:#66d9ef">global</span> count
    <span style="color:#66d9ef">if</span> count<span style="color:#f92672">&lt;</span><span style="color:#ae81ff">1</span>:
        <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#34;线程:{}--没抢到，票抢完了&#34;</span><span style="color:#f92672">.</span>format(i))
        <span style="color:#66d9ef">return</span>
    count<span style="color:#f92672">-=</span><span style="color:#ae81ff">1</span>
    <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#34;线程:{}--抢到一张票，还剩{}张票&#34;</span><span style="color:#f92672">.</span>format(i,count))
    release_lock(<span style="color:#e6db74">&#39;resource&#39;</span>,identifier)


<span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(<span style="color:#ae81ff">50</span>):
    t <span style="color:#f92672">=</span> Thread(target<span style="color:#f92672">=</span>seckill,args<span style="color:#f92672">=</span>(i,))
    t<span style="color:#f92672">.</span>start()
</code></pre></div>
</div>


    </main>

    
  </body>
</html>
